/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024.
 * Description: early fiq lastwork for dog exceed happened when system at the beginning process.
 * Author: heyang <heyang@huawei.com>
 * Create: 2024-08-15
 */

#include <asm/ptrace.h>
#include <linux/slab.h>
#include "fiq_glue_private.h"
#include "fiq_routeway_register.h"

static DEFINE_SPINLOCK(rtos_early_fiq_spinlock);
static uint32_t fiq_glue_params[FIQ_GLUE_NUM_MAX][FIQ_GLUE_INT_PARAM_MAX];

static int fiq_glue_parse_source(struct device_node *np)
{
	int err;
	const char *string = NULL;

	fiq_priv.fiq_source = FIQ_UNKNOWN_SOURCE;
	err = of_property_read_string(np, FIQ_SOURCE_STRING, &string);
	fiq_glue_log_and_return_if(err, err, "%s %s does not exist", __func__, FIQ_SOURCE_STRING);

	return fiq_glue_parse_source_common(string);
}

static int fiq_glue_parse_type(struct device_node *np)
{
	int err;
	const char *string = NULL;

	fiq_priv.fiq_type = FIQ_UNKNOWN_TYPE;
	err = of_property_read_string(np, FIQ_TYPE_STRING, &string);
	fiq_glue_log_and_return_if(err, err, "%s %s does not exist", __func__, FIQ_TYPE_STRING);

	return fiq_glue_parse_type_common(string);
}

#if defined(CONFIG_ARM64) || defined(CONFIG_RTOS_HAL_FIQ_ARM_WITH_TF)
static void fiq_glue_parse_buffer_offset_type(struct device_node *np)
{
	if (of_property_present(np, FIQ_CPUID_IS_CONTINUOUS)) {
		cpuid_is_continuous = true;
		pr_info("%s fiq get cpu id type is general!\n", __func__);
	} else {
		cpuid_is_continuous = false;
		pr_info("%s fiq get cpu id type is default!\n", __func__);
	}
}
#else
static void fiq_glue_parse_buffer_offset_type(struct device_node *np) {}
#endif

static void fiq_glue_parse_clean_wdt(struct device_node *np)
{
	(void)of_property_read_u32(np, FIQ_CLEAN_WTG, &fiq_priv.fiq_clean_wtg);
	pr_info("%s fiq clean-wdt is %u!\n", __func__, fiq_priv.fiq_clean_wtg);
}

static void fiq_glue_parse_dead_loop(struct device_node *np)
{
	fiq_priv.fiq_deadloop = true;
	(void)of_property_read_u32(np, FIQ_DEADLOOP, &fiq_priv.fiq_deadloop);
	pr_info("%s fiq dead-loop is %u!\n", __func__, fiq_priv.fiq_deadloop);
}

static void fiq_glue_parse_invalid_cache(struct device_node *np)
{
	(void)of_property_read_u32(np, FIQ_NEED_INVALID_CACHE, &fiq_priv.need_invalid_cache);
	pr_info("%s fiq invalid-cache is %u!\n", __func__, fiq_priv.need_invalid_cache);
}

static int fiq_glue_no_info_init(struct device_node *np)
{
	int err;
	uint32_t i;

	err = of_property_read_u32(np, "fiq_nums", &g_fiq_glue_no_info.fiq_num);
	fiq_glue_log_and_return_if(err, err, "%s fiq_nums does not exist", __func__);

	pr_info("%s fiq_num is %u, fiq_num_max is %d\n",
		__func__, g_fiq_glue_no_info.fiq_num, FIQ_GLUE_NUM_MAX);

	if (g_fiq_glue_no_info.fiq_num > FIQ_GLUE_NUM_MAX)
		return -E2BIG;

	memset(&fiq_glue_params[0][0], 0xFF, sizeof(fiq_glue_params));
	of_property_read_u32_array(np, "interrupts", &fiq_glue_params[0][0],
		FIQ_GLUE_INT_PARAM_MAX * g_fiq_glue_no_info.fiq_num);
	fiq_glue_log_and_return_if(err, err, "%s fail to parse", __func__);

	for (i = 0; i < g_fiq_glue_no_info.fiq_num; i++) {
		g_fiq_glue_no_info.fiq_no[i] = fiq_glue_no_translate(fiq_glue_params[i]);
		pr_info("%s i %u fiq-no %u\n", __func__, i, g_fiq_glue_no_info.fiq_no[i]);
	}

	return 0;
}

static int early_fiq_parse(struct device_node *np)
{
	int err;

	err = fiq_glue_parse_source(np);
	if (err)
		return err;

	err = fiq_glue_parse_type(np);
	if (err)
		return err;

	fiq_glue_parse_buffer_offset_type(np);
	fiq_glue_parse_clean_wdt(np);
	fiq_glue_parse_dead_loop(np);
	fiq_glue_parse_invalid_cache(np);

	return fiq_glue_no_info_init(np);
}

void fiq_early_panic_callback(unsigned int fiq_num)
{
	spin_lock(&rtos_early_fiq_spinlock);
	pr_info("eFiq core%d\n", smp_processor_id());
	dump_stack();
	pr_info("---eFiq---\n");
	spin_unlock(&rtos_early_fiq_spinlock);
}

void unregister_early_fiq_callback_node(struct fiq_num_node *fiq_node)
{
	struct fiq_callback_node *callback_node = NULL;
	struct list_head *t = NULL;
	struct list_head *n = NULL;

	list_for_each_safe(t, n, &fiq_node->callback_head) {
		callback_node = list_entry(t, struct fiq_callback_node, node);
		if (callback_node->callback == fiq_early_panic_callback) {
			list_del(&callback_node->node);
			kfree(callback_node);
		}
	}
}

void __init early_fiq_lastword_init(void)
{
	struct device_node *np;

	np = of_find_compatible_node(NULL, NULL, FIQ_DTS_COMPATIBLE_STRING);
	if (!np) {
		pr_info("%s hisi_fiq not exist\n", __func__);
		goto err;
	}

	if (!of_property_present(np, FIQ_EARLY_MODE_ENABLE_STRING)) {
		g_fiq_early_mode = EARLY_FIQ_DISABLED;
		goto err;
	}

	if (early_fiq_parse(np)) {
		pr_info("%s hisi_fiq parse fail\n", __func__);
		goto err;
	}
	hisi_request_fiq(fiq_early_panic_callback);
	pr_info("eFiq load done\n");
	g_fiq_early_mode = EARLY_FIQ_REGISTER_BEFORE;

err:
	of_node_put(np);
}
